use base64::{self, engine::general_purpose, Engine};
use chrono;
use crc;
use serde::{Deserialize, Serialize};
use soft_aes::aes::{aes_dec_ecb, aes_enc_ecb};
use std::process::Command;
use uuid;
// use sysinfo::{System,Disks};

#[derive(Serialize, Deserialize)]
struct TokenV1 {
    ver: String,
    uuid: String,
    token: String,
    sys_info: Vec<String>,
}

#[derive(Clone, Default, serde::Serialize, Debug)]
pub struct LiCoreV1 {
    sys_version: String,
    act_aes_key: [u8; 16],
    act_aes_padding: Option<&'static str>,
    lic_aes_key: [u8; 16],
    lic_aes_padding: Option<&'static str>,
}

impl LiCoreV1 {
    pub fn new() -> LiCoreV1 {
        let sys_version = "LiCoreV1.0.0".to_string();
        let act_aes_key: [u8; 16] = *b"OddStringKey59bd";
        let act_aes_padding = Some("PKCS7");
        let lic_aes_key: [u8; 16] = *b"OddStringKey4688";
        let lic_aes_padding = Some("PKCS7");
        return LiCoreV1 {
            sys_version,
            act_aes_key,
            act_aes_padding,
            lic_aes_key,
            lic_aes_padding,
        };
    }

    pub fn gen_smblos_uuid(&self) -> String {
        // 还要有一个字段是判断是center还是client
        return uuid::Uuid::new_v4().to_string();
    }

    // 获取bios的uuid  [wmic csproduct get UUID]
    pub fn get_smbios_uuid(&self) -> String {
        // 这边要做错误回收
        let output = Command::new("wmic")
            .arg("csproduct")
            .arg("get")
            .arg("UUID")
            .output()
            .unwrap();
        let output = String::from_utf8(output.stdout).unwrap();
        if let Some(index) = output.find('\n') {
            // 删除第一行并去除首尾的空白字符
            let my_uuid = uuid::Uuid::parse_str(&output[index + 1..].trim()).unwrap();
            return my_uuid.to_string();
        } else {
            return "null".to_string();
        }
    }

    // 生成激活token--带有时间信息
    pub fn gen_actoken(&self) -> String {
        let local_time = chrono::Local::now().timestamp();
        let uuid = self.gen_smblos_uuid();
        let token_str = format!("{};{};{};", self.get_smbios_uuid(), uuid, local_time);
        let encrypted: Vec<u8> = aes_enc_ecb(
            token_str.as_bytes(),
            &self.act_aes_key,
            self.act_aes_padding,
        )
        .expect("Actoken Encryption failed");
        let token = general_purpose::STANDARD.encode(&encrypted);
        let token_pack = TokenV1 {
            ver: self.sys_version.clone(),
            uuid,
            token,
            sys_info: vec![],
        };
        let payload = serde_json::to_string(&token_pack).unwrap();
        let encode_payload = general_purpose::STANDARD.encode(&payload);
        format!("{encode_payload}")
    }

    pub fn gen_actokey(&self, raw_token: &str) -> (String, String) {
        let decode_payload = general_purpose::STANDARD.decode(&raw_token).unwrap();
        let json_payload = String::from_utf8(decode_payload).unwrap();
        let actoken: TokenV1 = serde_json::from_str(&json_payload).unwrap();
        println!("uuid:{}", actoken.uuid);
        let decode_token = general_purpose::STANDARD.decode(&actoken.token).unwrap();
        let decode_token = aes_dec_ecb(&decode_token, &self.act_aes_key, self.act_aes_padding)
            .expect("Actokey Decryption failed");
        let token_str = String::from_utf8(decode_token).unwrap();
        const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC);
        let actokey = format!("{:04X}", X25.checksum(&token_str.as_bytes()));
        (format!("{actokey}"), actoken.uuid)
    }

    pub fn generate_lic(&self, uuid: String) -> String {
        let local_time = chrono::Local::now().timestamp();
        let license_str = format!("{};{};{};", local_time, uuid, self.get_smbios_uuid());
        let encrypted: Vec<u8> = aes_enc_ecb(
            license_str.as_bytes(),
            &self.lic_aes_key,
            self.lic_aes_padding,
        )
        .expect("License Encryption failed");
        let token = general_purpose::STANDARD.encode(&encrypted);
        let token_pack = TokenV1 {
            ver: self.sys_version.clone(),
            uuid,
            token,
            sys_info: vec![],
        };
        let payload = serde_json::to_string(&token_pack).unwrap();
        let encode_payload = general_purpose::STANDARD.encode(&payload);
        format!("{encode_payload}")
    }

    pub fn verify_lic(&self, lic_str: String) -> bool {
        let decode_payload = general_purpose::STANDARD.decode(&lic_str).unwrap();
        let json_payload = String::from_utf8(decode_payload).unwrap();
        let lic_token: TokenV1 = serde_json::from_str(&json_payload).unwrap();
        let smblos_uuid: String = lic_token.uuid;
        let decode_token = general_purpose::STANDARD.decode(&lic_token.token).unwrap();
        let decode_token = aes_dec_ecb(&decode_token, &self.lic_aes_key, self.lic_aes_padding)
            .expect("Lictoken Decryption failed");
        let token_str = String::from_utf8(decode_token).unwrap();
        let smbios_uuid = self.get_smbios_uuid();
        let compare_str = format!("{};{};", smblos_uuid, smbios_uuid);
        let compare_result = token_str.contains(&compare_str);
        return compare_result;
    }

    // pub fn get_sys_info(self) {
    //     let disks = Disks::new_with_refreshed_list();
    //     for disk in &disks {
    //         println!("{disk:?}");
    //     }
    // }
}
